package com.dibris.macco.applicationsreader;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.GZIPOutputStream;

import android.app.ListActivity;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.security.KeyChain;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.gson.Gson;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Base64;


public class AllAppsActivity extends ListActivity {
    private PackageManager packageManager = null;
    private List<PInfo> applist = null;
    private ArrayList<PInfo2> applistwithoutimgs = new ArrayList<PInfo2>();
    private ApplicationAdapter listadaptor = null;
    private TextView infoText = null;
 //   private Button putOfflineButton = null;
 //   private Button genCertificateButton = null;
   private Button reqCertificateTrue = null;
    private Button reqCertificateFalse = null;
    private final String DEVICE_ID= Settings.Secure.ANDROID_ID;

    private void copyList(List<PInfo> start, List<PInfo2> end){
        for(PInfo element : start){
            PInfo2 newElem = new PInfo2();
            newElem.permissionInfos=(ArrayList<PermissionInfo>)element.permissionInfos.clone();
            newElem.appname=element.appname;
            newElem.pname=element.pname;
            newElem.versionName=element.versionName;
            end.add(newElem);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_all_apps);

        packageManager = getPackageManager();
        infoText = (TextView)findViewById(R.id.infoText);
   /*     putOfflineButton=(Button)findViewById(R.id.putOfflineButton);
        putOfflineButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
                wifi.setWifiEnabled(false);
                setMobileDataEnabled(getApplicationContext(), false);
                BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
                mBluetoothAdapter.disable();
            }
        });
*/
        reqCertificateTrue=(Button)findViewById(R.id.button_req_cert_T);
        reqCertificateFalse =(Button)findViewById(R.id.button_req_cert_f);

        reqCertificateTrue.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                copyList(applist,applistwithoutimgs);
                String appsJson = new Gson().toJson(applistwithoutimgs);
                String policyJson = new Gson().toJson(new String [] {"P1", "P2", "P3"});
                new PostData().execute(appsJson,policyJson);
            }
        });


        reqCertificateFalse.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                copyList(applist,applistwithoutimgs);
                String appsJson = new Gson().toJson(applistwithoutimgs);
                String policyJson = new Gson().toJson(new String[]{"P1", "P2", "P3", "P4"});
                new PostData().execute(appsJson,policyJson);
            }
        });

  /*      genCertificateButton=(Button)findViewById(R.id.button);
        genCertificateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int days = 365;
                Date startDate = new Date();
                Date endDate = new Date(startDate.getTime() + days * 86400000l);
                String dn="CN=Test, L=London, C=GB";
                BigInteger serialNum = BigInteger.valueOf(new SecureRandom().nextInt());
                X500Name issuer=new X500Name("CN=Test, OU=None, O=None L=None, C=None");
                KeyPairGenerator keyPairGenerator = null;
                try {
                    keyPairGenerator = KeyPairGenerator.getInstance("RSA");
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
                keyPairGenerator.initialize(1024);
                KeyPair certPubKey = keyPairGenerator.generateKeyPair();
                ContentSigner sigGen= null;
                try {
                    sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC").build(certPubKey.getPrivate());
                } catch (OperatorCreationException e) {
                    e.printStackTrace();
                }
                // Raw value to place in cert for OID 1.2.3.4.
                byte[] bytearray = {0, 0, 0, 0, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
                try {
                    bytearray=serializeList(applist);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // Start creating the certificate beginning with the TBS certificate.
                V3TBSCertificateGenerator tbsGen = new V3TBSCertificateGenerator();
                tbsGen.setSerialNumber(new ASN1Integer(serialNum));
                tbsGen.setIssuer(issuer);
                tbsGen.setStartDate(new Time(startDate));
                tbsGen.setEndDate(new Time(endDate));
                tbsGen.setSubject(new X500Name(dn));
                tbsGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(certPubKey.getPublic().getEncoded()));
                tbsGen.setSignature(sigGen.getAlgorithmIdentifier());

                // The Key Usage extension:
                X509KeyUsage keyuse = new X509KeyUsage(
                        X509KeyUsage.digitalSignature |
                                X509KeyUsage.nonRepudiation   |
                                X509KeyUsage.keyEncipherment  |
                                X509KeyUsage.dataEncipherment);
                Extension keyUsageExt =
                        null;
                try {
                    keyUsageExt = new Extension(
                            Extension.keyUsage,
                            true,
                            keyuse.getEncoded());
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // The Basic Constraints extension:
                BasicConstraints basic = new BasicConstraints(false);
                Extension basicExt =
                        null;
                try {
                    basicExt = new Extension(
                            Extension.basicConstraints,
                            false,
                            basic.getEncoded());
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // The Custom extension:
                ASN1ObjectIdentifier asn1iod =
                        new ASN1ObjectIdentifier("1.2.3.4.5");
                Extension customExt =
                        new Extension(
                                asn1iod,
                                false,
                                bytearray);

                Extension[] extArray = {keyUsageExt, basicExt, customExt};
                tbsGen.setExtensions(new Extensions(extArray));

                // Create the TBS certificate.
                TBSCertificate tbsCert = tbsGen.generateTBSCertificate();

                // Sign the certificate.
                OutputStream ostream       = sigGen.getOutputStream();
                DEROutputStream derOstream = new DEROutputStream(ostream);
                try {
                    derOstream.writeObject(tbsCert);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    ostream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                byte[] tbsSig = sigGen.getSignature();

                // Assemble the full X509 certificate. (TBS + Sig Alg + Sig)
                ASN1EncodableVector asnVector = new ASN1EncodableVector();
                asnVector.add(tbsCert);
                asnVector.add(sigGen.getAlgorithmIdentifier());
                asnVector.add(new DERBitString(tbsSig));
                X509CertificateHolder certHolder =
                        new X509CertificateHolder(
                                org.bouncycastle.asn1.x509.Certificate.getInstance(new DERSequence(asnVector)));

                try {
                    X509Certificate cert =
                            new JcaX509CertificateConverter()
                                    .setProvider("BC").getCertificate(certHolder);
                    String filename = "certTemp.crt";
                    File root = android.os.Environment.getExternalStorageDirectory();
                    File dir = new File(root.getAbsolutePath() + "/download");
                    dir.mkdirs();
                    File file = new File(dir, filename);
                    FileOutputStream outputStream;
                    String fileContent=convertToPem(cert);

                    try {
                        outputStream =new FileOutputStream(file);
                        PrintWriter pw = new PrintWriter(outputStream);
                        pw.print(fileContent);
                        pw.close();
                        outputStream.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } catch (CertificateException e) {
                    e.printStackTrace();
                }
            }
        });*/
        new LoadApplications().execute();

    }

    private class PostData extends AsyncTask<String, Void, Void>{
        private ProgressDialog progress = null;

        private void postData(String configJson, String configPolicy, String device) {
            // Create a new HttpClient and Post Header
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost("http://192.168.0.179/cert_req.php");

            try {
                // Add your data
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
                nameValuePairs.add(new BasicNameValuePair("device", device));
                nameValuePairs.add(new BasicNameValuePair("config", configJson));
                nameValuePairs.add(new BasicNameValuePair("policy", configPolicy));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                Log.i("S","start http post request");
                // Execute HTTP Post Request
                HttpResponse response = httpclient.execute(httppost);
                Log.i("S","end http post request");
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    try {
                        if(response.getStatusLine().getStatusCode()!=200){
                            return;
                        }
                        InputStream inputStream = entity.getContent();

                       ByteArrayOutputStream buffer = new ByteArrayOutputStream();

                        int nRead;
                        byte[] data = new byte[16384];

                        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
                            buffer.write(data, 0, nRead);
                        }

                        buffer.flush();

                        Intent intent = KeyChain.createInstallIntent();
                        intent.putExtra(KeyChain.EXTRA_PKCS12, buffer.toByteArray());
                        startActivity(intent);

                    } catch (IllegalStateException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
            } catch (IOException e) {
                // TODO Auto-generated catch block
            }
        }

        private byte[] readFile(String filename) throws Exception {
            File f = new File(Environment.getExternalStorageDirectory(), filename);
            byte[] result = new byte[(int) f.length()];
            FileInputStream in = new FileInputStream(f);
            in.read(result);
            in.close();

            return result;
        }

        private String convertStreamToString(InputStream is) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();

            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return sb.toString();
        }

        @Override
        protected Void doInBackground(String... json) {
            postData(json[0], json[1], DEVICE_ID);
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            progress.dismiss();
            super.onPostExecute(result);
        }

        @Override
        protected void onPreExecute() {
            progress = ProgressDialog.show(AllAppsActivity.this, null,
                    "Requesting certificate...");
            super.onPreExecute();
        }
    }

   private byte[] serializeList(List<?> list) throws IOException {
       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos);
       oos.writeObject(list);
       byte[] bytes = bos.toByteArray();
       return bytes;
   }

    protected static String convertToPem(X509Certificate cert) throws java.security.cert.CertificateEncodingException {
        Base64 encoder = new Base64();
        String cert_begin = "-----BEGIN CERTIFICATE-----\n";
        String end_cert = "-----END CERTIFICATE-----";

        byte[] derCert = cert.getEncoded();
        String pemCertPre = new String(encoder.encode(derCert));
        String pemCert = cert_begin + pemCertPre + end_cert;
        return pemCert;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        boolean result = true;

        switch (item.getItemId()) {

            default: {
                result = super.onOptionsItemSelected(item);

                break;
            }
        }

        return result;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        PInfo app = applist.get(position);
        try {
            Intent intent = packageManager
                    .getLaunchIntentForPackage(app.pname);

            if (null != intent) {
                startActivity(intent);
            }
        } catch (ActivityNotFoundException e) {
            Toast.makeText(AllAppsActivity.this, e.getMessage(),
                    Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(AllAppsActivity.this, e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }
    }

    private List<ApplicationInfo> checkForLaunchIntent(List<ApplicationInfo> list) {
        ArrayList<ApplicationInfo> applist = new ArrayList<ApplicationInfo>();
        for (ApplicationInfo info : list) {
            try {
                if (null != packageManager.getLaunchIntentForPackage(info.packageName)) {
                    applist.add(info);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return applist;
    }

    public static int compressString(String srcTxt) {
        ByteArrayOutputStream rstBao = new ByteArrayOutputStream();
        try {
            GZIPOutputStream zos = new GZIPOutputStream(rstBao);
            zos.write(srcTxt.getBytes());
            zos.close();
        }
        catch (IOException e){
            Log.d("error", e.getMessage());
        }

        byte[] bytes = rstBao.toByteArray();
        return bytes.length;
    }

    private class LoadApplications extends AsyncTask<Void, Void, String> {
        private ProgressDialog progress = null;
        private String str="";

        private ArrayList<PInfo> getPackages(boolean getSysPackages) { /* false = no system packages */
            ArrayList<PInfo> apps = getInstalledApps(getSysPackages);
            final int max = apps.size();
            for (int i=0; i<max; i++) {
                apps.get(i).prettyPrint();
            }
            return apps;
        }

        private ArrayList<PInfo> getInstalledApps(boolean getSysPackages) {
            ArrayList<PInfo> res = new ArrayList<PInfo>();
            long init=new Date().getTime();
            Log.d("temp", String.valueOf(init));
            //List<PackageInfo> packs = getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS|PackageManager.GET_CONFIGURATIONS|PackageManager.GET_PROVIDERS|PackageManager.GET_RECEIVERS|PackageManager.GET_SERVICES|PackageManager.GET_SIGNATURES|PackageManager.GET_SHARED_LIBRARY_FILES);
            List<PackageInfo> packs = getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS);
            for(int i=0;i<packs.size();i++) {
                PackageInfo p = packs.get(i);
                if ((!getSysPackages) && (null == p.versionName)) {
                    continue ;
                }
                PInfo newInfo = new PInfo();
                newInfo.appname = p.applicationInfo.loadLabel(getPackageManager()).toString();

                newInfo.pname = p.packageName;
                newInfo.versionName = p.versionName;
              /*  newInfo.versionCode = p.versionCode;*/
                newInfo.icon = p.applicationInfo.loadIcon(getPackageManager());
/*                newInfo.firstInstallTime=p.firstInstallTime;
                newInfo.lastUpdateTime=p.lastUpdateTime;*/
                if(p.permissions!=null)
                    for(int j=0; j<p.permissions.length; j++){
                        newInfo.permissionInfos.add(p.permissions[j]);
                    }
                /*if(p.configPreferences!=null)
                    for(int j=0; j<p.configPreferences.length; j++){
                        newInfo.configurationInfos.add(p.configPreferences[j]);
                    }
                if(p.providers!=null)
                    for(int j=0; j<p.providers.length; j++){
                        newInfo.providersInfos.add(p.providers[j]);
                    }
                if(p.receivers!=null)
                    for(int j=0; j<p.receivers.length; j++){
                        newInfo.receiversInfos.add(p.receivers[j]);
                    }
                if(p.reqFeatures!=null) {
                    for (int j = 0; j < p.reqFeatures.length; j++) {
                        newInfo.reqFeatures.add(p.reqFeatures[j]);
                    }
                }
                if(p.requestedPermissions!=null) {
                    for (int j = 0; j < p.requestedPermissions.length; j++) {
                        newInfo.requestedPermissions.add(p.requestedPermissions[j]);
                    }
                }
                if(p.signatures!=null)
                    for(int j=0; j<p.signatures.length; j++){
                        newInfo.signatures.add(p.signatures[j]);
                    }
                newInfo.dataDirs=p.applicationInfo.dataDir;
                newInfo.publicSourceDirs=p.applicationInfo.publicSourceDir;
                newInfo.sourceDirs=p.applicationInfo.sourceDir;
                if(p.applicationInfo.sharedLibraryFiles!=null)
                    for(int j=0; j<p.applicationInfo.sharedLibraryFiles.length; j++){
                        newInfo.sharedLibraryFiles.add(p.applicationInfo.sharedLibraryFiles[j]);
                    }*/
                res.add(newInfo);
            }
            long end=new Date().getTime();
            Log.d("temp", "End: "+String.valueOf(end)+" Diff: "+String.valueOf(end-init)+" N# app: "+packs.size());
            int compDim=compressString(res.toString());
            int uncomp=res.toString().getBytes().length;
            str="Time: " + String.valueOf((end - init)) + " ms. Apps: " + packs.size() ;//+ " Uncompressed dim: " + uncomp + " GZipped dim: " + compDim;


            return res;
        }



        @Override
        protected String doInBackground(Void... params) {
            applist = getPackages(true);
            listadaptor = new ApplicationAdapter(AllAppsActivity.this,
                    R.layout.snippet_list_row, applist);
            return str;
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }

        @Override
        protected void onPostExecute(String result) {
            setListAdapter(listadaptor);
            progress.dismiss();
            infoText.setText(result);
            super.onPostExecute(result);
        }

        @Override
        protected void onPreExecute() {
            progress = ProgressDialog.show(AllAppsActivity.this, null,
                    "Loading application info...");
            super.onPreExecute();
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }
    }

    private void setMobileDataEnabled(Context context, boolean enabled)  {
        final ConnectivityManager conman = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        Class conmanClass = null;
        try {
            conmanClass = Class.forName(conman.getClass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Field iConnectivityManagerField = null;
        try {
            iConnectivityManagerField = conmanClass.getDeclaredField("mService");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        iConnectivityManagerField.setAccessible(true);
        Object iConnectivityManager = null;
        try {
            iConnectivityManager = iConnectivityManagerField.get(conman);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        Class iConnectivityManagerClass = null;
        try {
            iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method setMobileDataEnabledMethod = null;
        try {
            setMobileDataEnabledMethod = iConnectivityManagerClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        setMobileDataEnabledMethod.setAccessible(true);

        try {
            setMobileDataEnabledMethod.invoke(iConnectivityManager, enabled);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}